package ru.nsu.fit.iconveyor.conveyor.utils.jar;

import java.io.*;

import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * Class for loading jar file.
 *
 * @author Timur Zolotuhin (tzolotuhin@gmail.com)
 */
public class JarClassLoader extends ClassLoader {
	private HashMap<String, Class<?>> classes = new HashMap<String, Class<?>>();

    private String jarFileName;

	public JarClassLoader(String jarFileName) throws Exception {
		this.jarFileName = jarFileName;
		cacheClasses();
	}

	public synchronized Collection<String> getClasses() {
		return new ArrayList<String>(classes.keySet());
	}

	public synchronized Class<?> getClass(String name) throws ClassNotFoundException {
		Class<?> result = classes.get(name);

		if (result == null)
			result = super.findSystemClass(name);

		return result;
	}

//==============================================================================
//-----------------PRIVATE METHODS----------------------------------------------
    private void cacheClasses() throws Exception {
        File file = new File(jarFileName);
        URL[] urls = new URL[] {file.toURL()};
        URLClassLoader child = new URLClassLoader(urls, this.getClass().getClassLoader());

        JarFile jarFile = new JarFile(jarFileName);
        Enumeration<?> entries = jarFile.entries();
        while (entries.hasMoreElements()) {
            JarEntry jarEntry = null;
            try {
                jarEntry = (JarEntry) entries.nextElement();
                if (match(normalize(jarEntry.getName()))) {
                    String className = stripClassName(normalize(jarEntry.getName()));
                    Class classToLoad = Class.forName (className, true, child);
                    classes.put(classToLoad.getName(), classToLoad);
                    // define package
                    try {
                        int index = classToLoad.getName().lastIndexOf(".");
                        String packageName = "";
                        if (index >= 0)
                            packageName = classToLoad.getName().substring(0, index);
                        definePackage(packageName, "", "", "", "", "", "", null);
                    } catch (Throwable ex) {}
                }
            } catch (Throwable ex) {
                ex.printStackTrace();
            }
        }
    }

    private String stripClassName(String className) {
		return className.substring(0, className.length() - 6);
	}

	private String normalize(String className) {
        return className.replace("/", ".");
	}

	private boolean match(String className) {
		return className.endsWith(".class");
	}

	private byte[] loadClassData(JarFile jarFile, JarEntry jarEntry) throws IOException {
		long size = jarEntry.getSize();

		if (size == -1 || size == 0)
			return null;

		byte[] data = new byte[(int) size];
		InputStream in = jarFile.getInputStream(jarEntry);
		in.read(data);
		return data;
	}
}